//Linux C plus plus file
/************************************************************************
*
*
*       			FileName:log.cpp
*
*         			Author.wzj
*
*       			Email:345517039@qq.com
*
*      	    		Create:2017-06-22 23:44:55
*
*      		    	Description:-
*
*
*************************************************************************/
#include "log.h"


LogManger * LogManger::logMsg = NULL;
LogManger::Garbage LogManger::gg;


/*@brief: 获取单例模式实例;
 *
 *
 */
LogManger * LogManger::getInstance(){
	if ( LogManger::logMsg == NULL ){
		return  new LogManger();
	}
	return LogManger::logMsg;
}

/*@brief:Log Moduel Init
 *
 *
 */
int LogManger::LogInit(confMgr *conf, std::vector<int> &logVec ){
#define LOG_BUF_SIZE 200
#define LOG_BUF_MAX 1024
	int ret = RET::FAIL;
	time_t t;
	struct tm *local;
	ret = confMgr::getConfValue(LOG_CONFIG,&logconf,conf);
	if ( ret == RET::FAIL ){
		DEBUG_TRACE(2,("[ ERROR ]LOG MODUEL GET CONFIGURE OPTION FAILURE!\n"));
		return  ret;
	}
	logst.mem.init(SYSTEM_LEVEL);
    logst.accessLogFd = logst.errorLogFd = -1; 
	logst.accessLogPath = (char *)logst.mem.allocateMemFromMempool(LOG_BUF_SIZE);
	logst.errorLogPath = (char *)logst.mem.allocateMemFromMempool(LOG_BUF_SIZE);
	GETTIME(local,t);
	DEBUG_TRACE(2,("[ INFO ]CURRENT TIME: YEAR:%d  MON: %d DAY: %d \n", local->tm_year + 1900, local->tm_mon + 1, local->tm_mday));
	sprintf(logst.accessLogPath, "%s%d%d%dAccess.log",logconf.pAccessLogPath,local->tm_year + 1900, local->tm_mon + 1, local->tm_mday);
	sprintf(logst.errorLogPath, "%s%d%d%dError.log",logconf.pErrorLogPath,local->tm_year + 1900, local->tm_mon + 1, local->tm_mday);
	DEBUG_TRACE(2,("[ INFO ]ACCESS LOG PATH:%s\n", logst.accessLogPath));
	DEBUG_TRACE(2,("[ INFO ]ERROR LOG PATH:%s\n", logst.errorLogPath));
	logst.logLevel = logconf.iWriteLogLevel;	
	if (logst.accessLogFd == -1 ){
		if (access(logst.accessLogPath, F_OK)!=0 ){
			logst.accessLogFd = open(logst.accessLogPath, O_APPEND|O_CREAT|O_RDWR, 0664);
			if ( logst.accessLogFd < 0 ){
				DEBUG_TRACE(2,("[ ERROR ]CREATE ACCESS LOG ERROR\n"));
				return RET::FAIL;
			}
		}else{
			DEBUG_TRACE(2,("[ INFO ]LOCAL PC HAS FIND ACCESS LOG FILE!\n"));
			logst.accessLogFd = open(logst.accessLogPath, O_APPEND|O_RDWR, 0664);
			if ( logst.accessLogFd < 0 ){
				DEBUG_TRACE(2,("[ ERROR ]OPEN ACCESS LOG ERROR\n"));
				return RET::FAIL;
			}
		}
	}
	if (logst.errorLogFd == -1 ){
		if (access(logst.errorLogPath, F_OK)!=0 ){
			logst.errorLogFd = open(logst.errorLogPath, O_APPEND|O_CREAT|O_RDWR, 0664);
			if ( logst.errorLogFd < 0 ){
				DEBUG_TRACE(2,("[ ERROR ]CREATE ERROR LOG ERROR\n"));
				return RET::FAIL;
			}
		}else{
			DEBUG_TRACE(2,("[ INFO ]LOCAL PC HAS FIND ERROR LOG FILE!\n"));
			logst.errorLogFd = open(logst.errorLogPath, O_APPEND|O_RDWR, 0664);
			if ( logst.errorLogFd < 0 ){
				DEBUG_TRACE(2,("[ ERROR ]OPEN ERROR LOG ERROR\n"));
				return RET::FAIL;
			}
		}
	}
	logst.logbuf =(char *)logst.mem.allocateMemFromMempool(LOG_BUF_MAX);
	if(!logst.logbuf){
		fprintf(stderr, "[ ERROR ] ALLOC MEM FORM MEM POOL FAILURE!\n");
		return RET::FAIL;
	}
	logst.bufSize = LOG_BUF_MAX;
	//Log Thread IO INIT
	logst.io = new epollModel();
	logst.ioType = EPOLL_MODEL;
	logst.io->init();
	logst.stopFlag = 0;
	struct server_conf_st serverconf;
	ret = confMgr::getConfValue(SYSTEM_CONFIG,&serverconf,conf);
	if ( ret == RET::FAIL ){
		DEBUG_TRACE(2,("[ ERROR ]GET SYSTEM CONFIG FAILURE!"));
		return ret;
	}
	int threadNum = serverconf.listenThreadNum + serverconf.processThreadNum;
	DEBUG_TRACE(2,("[ INFO ]SYSTEM HAS %d THREAD!\n", threadNum));
	for ( int i = 0; i < threadNum; i++){
		int fd[2] = {0};
		ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
		if ( ret == RET::FAIL ){
			DEBUG_TRACE(2,("[ ERROR ]CREATE SOCKET PAIR FAILURE IN FUNCTION:%s LINE:%d\n",__func__ ,__LINE__));
			return ret;
		}
		logst.io->addEvent(fd[1]);
		logVec.push_back(fd[0]);
		DEBUG_TRACE(2,("[ INFO ]SOCKET:%d PUT INTO THE VECTOR!\n", fd[0]));
	}
	//create a pthread called Log 
	ret = pthread_create(&(logst.logtid),NULL,LogManger::LogWriteRun, (void *)&logst);
	if ( ret < 0 ){
		DEBUG_TRACE(2,("[ ERROR ]CREATE LOG PTHREAD FAILURE IN FUNCTION:%s LINE:%d\n",__func__,__LINE__));
		return RET::FAIL;
	}
	DEBUG_TRACE(2,("[ INFO ]CREATE LOG PTHREAD TID:%lu SUCCESS!\n", syscall(SYS_gettid)));
	return RET::SUCCESS;
}

/*@brief:
 *@para:int fd:发送端的套接字 
 *		int type:事件类型; NETWORK_CONNECTION , SYSTEM_INIT, ERROR , ....
 *		const char * format:可变参数信息,
 *
 *@return value:
 */
int LogManger::LogFormatAndSend(int fd, int type, char *pbuf, const char *format,...){
	memset(pbuf, '\0', 1024);
	ssize_t writeLength = 0;
	ssize_t currentLogLength = 0;
	time_t t;
	struct tm * local;
	va_list ap;
	const char * eventSet[]={
		"[NETWORK CONNECTION]",
		"[INIT]",
		"[ERROR]",
		"[RUN]",
		NULL
	};
	//check parameter;
	if ( fd < 0 || type < 0 || (pbuf == NULL )){
		DEBUG_TRACE(2,("[ ERROR ]GET PARA IN FUNCTION:%s ERROR!", __func__));
		return  RET::FAIL;
	}
	switch(type){
		case NETWORKCONNECTION:
			 pbuf[0]='1';
			 break;
		case INIT:
			 pbuf[0]='1';
			 break;
		case RUN:
			 pbuf[0]='1';
			 break;
		case ERROR:
			 pbuf[0]='2';
			break;
		default:
			break;
	}
	currentLogLength += sprintf(pbuf+strlen(pbuf),"%s ", eventSet[type]);
	//get time 
    GETTIME(local,t);
	currentLogLength += sprintf(pbuf+strlen(pbuf),"%d:%d:%d:%d:%d:%d ",local->tm_year+1900, local->tm_mon+1, local->tm_mday,local->tm_hour, local->tm_min, local->tm_sec);
	va_start(ap,format);
	while(*format){
		switch(*format++){
			case 's':
				{
					char *logstring = NULL;
					logstring=va_arg(ap, char *);
					currentLogLength += sprintf(pbuf + strlen(pbuf), "%s ", logstring);
				}
				break;
			case 'd':
				{
					int logdata = 0;
					logdata = va_arg(ap, int);
					currentLogLength += sprintf(pbuf + strlen(pbuf), "%d ", logdata);
				}
				break;
			case 'u':
				{
					unsigned int ulogdata = 0;
					ulogdata = va_arg(ap, unsigned int);
					currentLogLength += sprintf(pbuf + strlen(pbuf), "%u ", ulogdata);
				}
				break;
			default:
				DEBUG_TRACE(2,("[ ERROR ]UNKNOWN FORMAT FIND IN FUNCTION:%s LINE:%d\n", __func__, __LINE__));
				return RET::FAIL;
		}
	}
	va_end(ap);
	pbuf[++currentLogLength]='\n';
	//write data the log socket 
	//we will set nonblock 
	writeLength=send(fd,pbuf,(int)currentLogLength,0);
	if(writeLength == currentLogLength){
		DEBUG_TRACE(2,("[ INFO ]LOG INFO SEND SUCCESSFULLY!"));
		return RET::SUCCESS;
	}
	return RET::FAIL;
}

/**
 * @brief: 日志线程的回调函数
 * @para: void *arg:
 * @author:wzj 
 * @return:
 */
void * LogManger::LogWriteRun(void *arg){
	prctl(PR_SET_NAME, "LogWrite");
	int count = 0;
	int iRet = RET::FAIL;
	LogManger * log = (LogManger *)arg;
	struct logManger_st logctx = log->getLogOptCtx();
	while(!logctx.stopFlag){
		count = logctx.io->waitForEvent();
		if(count <= 0){
			continue;
		}else{
			iRet = log->handleLogWrite(count);
			if(iRet == RET::FAIL){
				DEBUG_TRACE(2,("LOG THREAD EXIT ABNORMALLY!\n"));
				logctx.stopFlag = 1;
			}
		}
	}
	return log;
}

/**
 *@brief:日志线程处理函数;
 *
 *
 */
int LogManger::handleLogWrite(int count){
	int ioType = logst.ioType;
	ssize_t currRecv = 0;
	switch(ioType){
		case EPOLL_MODEL:
			{
				EPOLL_CTX * epollctx = (EPOLL_CTX *)logst.io->getIoCtx();
				for(int index = 0; index < count; index++){
					memset(logst.logbuf, '\0', logst.bufSize);
					if(epollctx->events[index].events & EPOLLIN){
						currRecv = recv(epollctx->events[index].data.fd, logst.logbuf, logst.bufSize, 0);
						if(currRecv > 0){
							DEBUG_TRACE(3,("[ LOG INFO ]%s\n", logst.logbuf));
						}
					}
				}
			}
			break;
		case SELECT_MODEL:
			{

			}
			break;
		case POLL_MODEL:
			{

			}
			break;
		default:
			{
				fprintf(stderr,"[ ERROR ]UNKNOWN IO MODEL TYPE SUPPORT!\n");
				return RET::FAIL;
			}
	}
	return RET::SUCCESS;
}
